﻿#include "transmitter.h"

Transmitter *Transmitter::instance = nullptr;

Transmitter::Transmitter(Operation *operation, OperationThread *operationThread) {
    this->operation = operation;
    this->operationThread = operationThread;
    this->signalGun = new SignalGun();
    startServer();
}

Transmitter *Transmitter::getInstance() {
    if (instance == nullptr) {
        ClientOperation *operation = new ClientOperation();
        ClientOperationThread *operationThread = new ClientOperationThread();
        instance = new Transmitter(operation, operationThread);
    }
    return instance;
}

void Transmitter::newTcpConnection() {
    SocketContainer *container = new SocketContainer();
    QTcpSocket *socket = server->nextPendingConnection();

    connect(socket, SIGNAL(readyRead()), this, SLOT(readTcp()));
    connect(socket, SIGNAL(disconnected()), this, SLOT(disconnect()));
    connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(error(QAbstractSocket::SocketError)));

    container->setSocket(socket);
    list.append(container);
}

void Transmitter::readUdp() {
    while (udpSocket->hasPendingDatagrams()) {
        QByteArray bytes;
        QHostAddress senderIp;
        quint16 senderPort;
        bytes.resize(udpSocket->pendingDatagramSize());
        udpSocket->readDatagram(bytes.data(), bytes.size(),
                                &senderIp, &senderPort);

        qDebug() << "senderIp: " << senderIp;
        qDebug() << "senderPort: " << senderPort;

        QDataStream out(&bytes, QIODevice::ReadOnly);
        qint16 headSize;
        out >> headSize;
        QByteArray infoBytes;
        infoBytes.resize(headSize);
        out.readRawData(infoBytes.data(), infoBytes.size());

        auto infoHead = InfoHead::generateInfo(infoBytes);

        QByteArray data;
        data.resize(infoHead->getFileSize());
        out.readRawData(data.data(), data.size());

        operation->exec(infoHead, data, senderIp, senderPort);
    }
}

void Transmitter::readTcp() {
    for (SocketContainer* container : list) {
        QTcpSocket *socket = container->getSocket();

        if (socket->bytesAvailable() <= 0) continue;

        QDataStream in(socket);
        if (!container->isInfoSizeReady() && socket->bytesAvailable() >= (int)sizeof (qint16)) {
            qint16 infoSize;
            in >> infoSize;
            container->setInfoSize(infoSize);
            container->infoSizeReady();
        }

        if (container->isInfoSizeReady() && !container->isInfoReady() &&
            socket->bytesAvailable() >= container->getInfoSize()) {
            uint infoSize = container->getInfoSize();

            QByteArray bytes;
            bytes.resize(infoSize);
            in.readRawData(bytes.data(), bytes.size());
            container->setInfoHead(InfoHead::generateInfo(bytes));
            container->infoReady();
        }

        if (container->isInfoReady() && socket->bytesAvailable() >= container->getInfoHead()->getFileSize()) {
            operationThread->setInfoHead(container->getInfoHead());
            operationThread->setDataStream(&in);
            operationThread->run();
        }
    }
}

void Transmitter::disconnect() {
    int i = 0;
    for (SocketContainer* container : list) {
        QTcpSocket *socket = container->getSocket();
        if (socket->state() == QTcpSocket::UnconnectedState) {
            socket->deleteLater();
            list.removeAt(i);
        }
        i ++;
    }
}

void Transmitter::startServer() {
    server = new QTcpServer();
    udpSocket = new QUdpSocket();
    udpSocket->bind(7777, QUdpSocket::ShareAddress);
    testMsg = "";

    connect(udpSocket, SIGNAL(readyRead()), this, SLOT(readUdp()));
    connect(server, SIGNAL(newConnection()), this, SLOT(newTcpConnection()));

    int port = 6666;
    server->listen(CONF.localIp, port);
}

void Transmitter::writeUdp(InfoHead *infoHead, QByteArray data, QHostAddress ip, qint16 port) {
    QByteArray bytes = infoHead->toBytes();
    bytes.append(data);
    udpSocket->writeDatagram(bytes.data(), bytes.size(), ip, port);
}

void Transmitter::writeTcp(InfoHead *infoHead, QByteArray data, QHostAddress ip, qint16 port) {
    QByteArray bytes = infoHead->toBytes();
    bytes.append(data);
    QTcpSocket *tcpSocket = new QTcpSocket();
    tcpSocket->connectToHost(ip, port);
    tcpSocket->waitForConnected();
    tcpSocket->write(bytes);
    tcpSocket->waitForBytesWritten();
    tcpSocket->disconnect();
}

void Transmitter::error(QAbstractSocket::SocketError err) {
    qDebug() << err;
}

SignalGun *Transmitter::getSignalGun() const
{
    return signalGun;
}

Operation *Transmitter::getOperation() const
{
    return operation;
}
